//
//  GASocketOper.m
//  Stock_Pro
//
//  Created by  on 11-11-16.
//  Copyright 2011年 gaotime. All rights reserved.
//

#import "GASocketOper.h"
#import "Tools.h"

@implementation GASocketOper

@synthesize m_pHeartTimer,m_pDataTimer;

/*
 pGAStream = [GAStream alloc];
 [pGAStream setDelegate:self];
 [pGAStream setServerIPPort:"www.baidu.com" withPort:80];
 [pGAStream connectToServer];
 */

- (id)init
{
  id obj = [super init];
  if (m_pGAStream==nil) {
    m_pGAStream = [GAStream alloc];
  }
  //没有连接
  m_nStep = kGASocketOperNoConnection;
  return obj;
}

- (void)setStatusNotice:(GASocketOperNoticeCallBack*)pNoticeCallBack
{
  m_pNoticeCallBack = pNoticeCallBack;
}

- (void)setUserIdAndSessionId:(long)alUserId withSession:(char*)apSessionId
{
  m_lUserId = alUserId;
  memcpy(m_strSessionId, apSessionId, sizeof(m_strSessionId));
}

- (void)setServerIPPort:(char*)apServer withPort:(int)anPort
{
  [m_pGAStream setServerIPPort:apServer withPort:anPort];
//  [m_pGAStream setServerIPPort:"218.206.72.56" withPort:8110];
}

- (void)connectToServer
{
  m_nStep = kGASocketOperWaitLoginKey;
  [m_pGAStream setDelegate:self];
  [m_pGAStream connectToServer];
}

- (BOOL)isHaveConnect
{
  if (m_nStep!=kGASocketOperNoConnection &&
      m_nStep!=kGASocketOperClose) {
    return YES;
  }
  return NO;
}

- (void)close
{
  if (self.m_pHeartTimer!=nil && [self.m_pHeartTimer isValid]) {
    [self.m_pHeartTimer invalidate];
    self.m_pHeartTimer = nil;
}
  
  if (self.m_pDataTimer!=nil && [self.m_pDataTimer isValid]) {
    [self.m_pDataTimer invalidate];
    self.m_pDataTimer = nil;
  }
  
  [m_pGAStream closeStream];
}

- (void)writeBytes:(char*)apSendBuffer length:(unsigned int)anSize
{
  if (m_nStep==kGASocketOperLoginComplete) {
    if (m_pSendData!=nil && [m_pSendData length]>0) {
      void *a_pData;
      a_pData = malloc([m_pSendData length]);
      [m_pSendData getBytes:a_pData];
      [m_pGAStream writeBytes:a_pData length:[m_pSendData length]];
      [m_pSendData setLength:0];
      free(a_pData);
    }
    [m_pGAStream writeBytes:apSendBuffer length:anSize];
  } else {
    if (m_pSendData==nil) {
      m_pSendData = [[NSMutableData alloc] init];
    }
    [m_pSendData appendBytes:apSendBuffer length:anSize];
  }
  
}

- (void)clearSendData
{
  if (m_pSendData!=nil) {
    [m_pSendData setLength:0];
  }
}

//生成socket密钥
- (void)CreateKey:(char*)buffer withCer:(char*)ap_cer
{
  char sKey[16];
  char sVet[16];
  int n;
  int nn;
  unsigned short sV1;
  unsigned short sV2;
  unsigned short sV;
  int i;
  
  memcpy( sKey, buffer , 16 );
  memset( sVet, 0, sizeof( sVet ) );
  strcpy( sVet, "shenglong" );
  n = strlen( "shenglong" );
  nn = 16 - n;
  memcpy( sVet + n, buffer + 16, nn );
  //char sCer[38];
  
  for ( i = 0; i < 16; i++ )
  {
    sV1 = ( unsigned char )sKey[i];
    sV2 = ( unsigned char )sVet[i];
    sV = sV1 * sV2;
    _s2b( sV, (ap_cer + ( i * 2 )+1) );
  }
  
  // 设置密匙包头
  ap_cer[0] = 0x04;     // 行情认证
}

- (void)sendLogin:(char*)apData
{
  char sCer[62];
  memset(sCer, 0, sizeof(sCer));
  
  Long64ToBytes(m_lUserId,sCer+38);
  memcpy(sCer+46,m_strSessionId,16);
  
  //切换成等待认证状态
  m_nStep = kGASocketOperWaitLoginStatus;
  
  //生成密钥返回给服务器
  [self CreateKey:apData+6 withCer:sCer+5];

  //发送数据给服务器
  //1+32+8+16;
  _i2b(62-5, sCer);
  sCer[4] = 0x01;
  [m_pGAStream writeBytes:sCer length:62];
}
#define SOCKET_CONNECT_STATUS_CODE_OK               0xFFFFFFFF
#define SOCKET_CONNECT_STATUS_CODE_NO_LOGIN             0xFFFFFFF0
#define SOCKET_CONNECT_STATUS_CODE_NO_LEVEL2            0xFFFFFFF1
#define SOCKET_CONNECT_STATUS_CODE_SESSION_TIME_OUT     0xFFFFFFF2
- (void)loginStatus:(char*)apData
{
  int nCode = 0;
  //偏移过socket 头
  nCode = _b2i(((char*)apData+6), 0);
  switch(nCode)
  {
    case SOCKET_CONNECT_STATUS_CODE_OK:
    {
      m_nStep = kGASocketOperLoginComplete;
      //回心跳给服务器
      [self sendHeart];
      //向上通知socket状态
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_CONN);
      }
      //如果队列中有数据，发送队列数据
      if (m_pSendData!=nil && [m_pSendData length]>0) {
        void *a_pData;
        a_pData = malloc([m_pSendData length]);
        [m_pSendData getBytes:a_pData];
        [m_pGAStream writeBytes:a_pData length:[m_pSendData length]];
        [m_pSendData setLength:0];
        free(a_pData);
      }
    }
      break;
    case SOCKET_CONNECT_STATUS_CODE_NO_LOGIN:
    {
      //没有登录
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_NO_LOGIN);
      }
    }
      break;
    case SOCKET_CONNECT_STATUS_CODE_NO_LEVEL2:
    {
      //没有leve2权限
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_NO_LEVE2);
      }
    }
      break;
    case SOCKET_CONNECT_STATUS_CODE_SESSION_TIME_OUT:
    {
      //session超时
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_SESSION_TIMEOUT);
      }
    }
      break;
  }
}

/**解析数据包，只做基础数据分发
 *
 *  @return
 *  @note 
 */
- (void)encryptBody
{
  //收到数据
  switch (m_nStep) {
    case kGASocketOperWaitLoginKey:
    {
      //printf("\nwait login key");
      [self sendLogin:m_pReadBodyBuffer];
    }
      break;
    case kGASocketOperWaitLoginStatus:
    {
      //printf("\nwait login status");
      [self loginStatus:m_pReadBodyBuffer];
      
      //如果定时器在运行，先停定时器
      if (self.m_pHeartTimer!=nil && [self.m_pHeartTimer isValid]) {
        [self.m_pHeartTimer invalidate];
        self.m_pHeartTimer = nil;
      }
      
      //启动定时器进行发心跳,30S
      self.m_pHeartTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(onTimerSendHeart:) userInfo:nil repeats:YES];
    }
      break;
    case kGASocketOperLoginComplete://认证成功
    {
      
      //printf("\nwait body");
      //回心跳给服务器
      [self sendHeart];
      //业务处理
      if (m_pNoticeCallBack && m_pNoticeCallBack->pResedData) {
        m_pNoticeCallBack->pResedData(m_pNoticeCallBack->pObject,
                                      m_pReadBodyBuffer,
                                      m_nBodySize);
      }
    }
      break;
  }
}

-(void)onCheckOutTime:(NSTimer *)t
{
  //如果定时器在运行，先停定时器
  if (self.m_pDataTimer!=nil && [self.m_pDataTimer isValid]) {
    [self.m_pDataTimer invalidate];
    self.m_pDataTimer = nil;
  }
  
  pEndData = [NSDate new];
  
  if (pBeginData != nil) {
    long long timeInterval = [self getDateTimeTOMilliSeconds:pEndData] - [self getDateTimeTOMilliSeconds:pBeginData];
    
    float useTime = timeInterval /1000.000;
    
    //NSLog(@"useTime %.3f",useTime);
    
    if (useTime < 10) {
      //网络正常
    }else{
      //网络超时，断链重连
//      [self close];
//      [self connectToServer];
      NSLog(@"网络超时，断链重连1");
      
      //连接出错,首先清理发送队列中的数据，然后向上通知状态
      m_nStep = kGASocketOperClose;
      [self clearSendData];
      //向上通知状态
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_ERROR);
      }
    }
    
  }else{
     //网络异常，断链重连
//    [self close];
//    [self connectToServer];
    NSLog(@"网络超时，断链重连2");
    
    //连接出错,首先清理发送队列中的数据，然后向上通知状态
    m_nStep = kGASocketOperClose;
    [self clearSendData];
    //向上通知状态
    if (m_pNoticeCallBack) {
      m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                   SOCKET_STATUS_ERROR);
    }
  }
}


-(void)onTimerSendHeart:(NSTimer*)t
{
  if (pBeginData != nil) {//30内有心跳返回过
    pBeginData = nil;
    
  }else{
    //启动定时器进行收数据,10S
    self.m_pDataTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(onCheckOutTime:) userInfo:nil repeats:NO];
  }
  
//	m_pHeartTimer = nil;
	[self sendHeart2];
}

//将NSDate类型的时间转换为NSInteger类型,从1970/1/1开始
-(long long)getDateTimeTOMilliSeconds:(NSDate *)datetime{
  NSTimeInterval interval = [datetime timeIntervalSince1970];
  //NSLog(@"interval=%f",interval);
  long long totalMilliseconds = interval*1000;
  //NSLog(@"totalMilliseconds=%llu",totalMilliseconds);
  return totalMilliseconds;
}


/**重置读取包头数据
 *
 *  @return
 *  @note 
 */
- (void)resetReadHead
{
  memset(m_cReadHeadBuffer, 0, sizeof(m_cReadHeadBuffer));
  m_nReadHeadSize = 0;
}

/**发送心跳包（收到数据回值00000）
 *
 *  @return
 *  @note 
 */
- (void)sendHeart
{
  char a_cBuffer[8];
  memset(a_cBuffer, 0, sizeof(a_cBuffer));
  [m_pGAStream writeBytes:a_cBuffer length:5];
}

- (void)sendHeart2
{
  char a_cBuffer[8];
  memset(a_cBuffer, 0, sizeof(a_cBuffer));
  a_cBuffer[4] = 4;//00004
  [m_pGAStream writeBytes:a_cBuffer length:5];
}


/**读取包头数据
 *
 *  @param  void      *pData      数据区
 *  @param  int       anSize      数据区大小
 *  @return
 *  @note 
 */
- (void)readHead:(void*)apData withSize:(int)anSize
{
  int nLen;
  
  pBeginData = [NSDate new];
  
  if (m_nReadHeadSize<5) {
    nLen = anSize>=5?5:anSize;
    if ((nLen + m_nReadHeadSize)>5) {
       nLen = 5 - m_nReadHeadSize;
    }
    memcpy(m_cReadHeadBuffer+m_nReadHeadSize, apData, nLen);
    m_nReadHeadSize += nLen;
    
    if (m_nReadHeadSize==5) {
      //包头读完，切换到读取包体状态
      m_nReadStatus = kGASocketOperReadBody;
      //后续包体大小
      m_nBodySize = _b2i0(m_cReadHeadBuffer);
      m_nReadBodySize = 0;
      
      if (m_cReadHeadBuffer[4]==0x04 || m_cReadHeadBuffer[4] == 0x00) {
      //if (m_cReadHeadBuffer[4] == 0x04) {
        //重置包头
        [self resetReadHead];
        //切成等待包头状态，并回心跳给服务器
        m_nReadStatus = kGASocketOperReadHead;
        [self sendHeart];
        //读下一包头
        if ((anSize-nLen)>0) {
          [self readHead:apData+nLen withSize:anSize-nLen];
        }
      } else {
        //把apData中剩余数据添到包体中
        if (m_pReadBodyBuffer) {
          Tools_Free(&m_pReadBodyBuffer);
        }
        //空间大小：包头＋包体＋2 (2是为了防止字符串数据越界)
        m_pReadBodyBuffer = Tools_Malloc(m_nBodySize+5+2);
        //复制包头
        memcpy(m_pReadBodyBuffer, m_cReadHeadBuffer, 5);
        //重置包头
        [self resetReadHead];
        //读取包体
        if ((anSize-nLen)>0) {
          [self readBody:apData+nLen withSize:anSize-nLen];
        }
      }
    }
  }
}

/**读取包体数据
 *
 *  @param  void      *pData      数据区
 *  @param  int       anSize      数据区大小
 *  @return
 *  @note 
 */
- (void)readBody:(void*)apData withSize:(int)anSize
{
  int nLen;
  
  if (m_nReadBodySize<m_nBodySize) {
    nLen = anSize>m_nBodySize?m_nBodySize:anSize;
    if ((nLen + m_nReadBodySize)>m_nBodySize) {
      nLen = m_nBodySize - m_nReadBodySize;
    }
    //目地地址要偏移过包头和以读数据
    char *pDes;
    pDes = m_pReadBodyBuffer+5+m_nReadBodySize;
    memcpy(pDes, apData, nLen);
    m_nReadBodySize += nLen;
    //printf("\nbody:%d\tread:%d\tlen:%d\tsize:%d",m_nBodySize,m_nReadBodySize,nLen,anSize);
    if (m_nReadBodySize==m_nBodySize) {
      //包体读满，切换到读取包头状态
      m_nReadStatus = kGASocketOperReadHead;
      //业务处理
      [self encryptBody];      
      //读取包头
      if ((anSize-nLen)>0) {
        [self readHead:apData+nLen withSize:anSize-nLen];
      }
    }
  }
}

- (void)gaStreamEvent:(GAStreamStatus)anStatus
             withData:(void*)apData
         withDataSize:(int)anSize
{
  //printf("\ngaStreamEvent:%d size:%d",anStatus,anSize);
  switch (anStatus) {
    case kGAStreamStatusComplete:
    {
      //连接成功
      m_nStep = kGASocketOperWaitLoginKey;
      m_nReadStatus = kGASocketOperReadHead;
      [self resetReadHead];
    }
      break;
    case kGAStreamStatusData:
    {
      switch (m_nReadStatus) {
        case kGASocketOperReadHead:
          //网络通信头
          [self readHead:apData withSize:anSize];
          break;
        case kGASocketOperReadBody:
          //数据包体
          [self readBody:apData withSize:anSize];
          break;
      }
    }
      break;
    case kGAStreamStatusClose:
    {
      //连接关闭,首先清理发送队列中的数据，然后向上通知状态
      m_nStep = kGASocketOperClose;
      [self clearSendData];
      //向上通知状态
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_ERROR);
      }
    }
      break;
    case kGAStreamStatusError:
    case kGAStreamStatusFailed:
    case kGAStreamStatusSystemError:
    {
      //连接出错,首先清理发送队列中的数据，然后向上通知状态
      m_nStep = kGASocketOperClose;
      [self clearSendData];
      //向上通知状态
      if (m_pNoticeCallBack) {
        m_pNoticeCallBack->pCallBack(m_pNoticeCallBack->pObject,
                                     SOCKET_STATUS_ERROR);
      }
    }
      break;
  }
}

@end
